package com.dk.controller;

import com.alibaba.fastjson.JSONObject;
import com.dk.data.Result;
import com.dk.em.ResultEnum;
import com.dk.entity.DtItem;
import com.dk.entity.DtLabel;
import com.dk.service.IDtItemService;
import com.dk.service.IDtLabelService;
import com.dk.util.JwtUtil;
import com.netflix.zuul.context.RequestContext;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.jta.WebLogicJtaTransactionManager;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;


@RestController
@RequestMapping("item")
@Api(value = "千人千面推荐管理接口")
public class ItemController {
    @Autowired
    private IDtItemService dtItemService;
    @Autowired
    private IDtLabelService dtLabelService;
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping
    public Result getItem(){
       List<DtItem> list = dtItemService.seleteAll();
       List<DtLabel> labelList = dtLabelService.selectAall();
       return Result.ok().setData(list);
    }

    @PostMapping("esGetItem")
    @ApiOperation(value = "推荐商品分页查询")
    //参数类型：paramType body表示@RequestBody接收的参数，path表示路径传参/{xx},query表示@RequestParam接受的参数
    @ApiImplicitParams({
            @ApiImplicitParam(name="pageNum",value = "当前页数",required = true,paramType = "query"),
            @ApiImplicitParam(name="pageSize",value = "每页条数",required = true,paramType = "query")
    })
    public Result esGetItem(HttpServletRequest request, @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10")Integer pageSize) throws IOException {

        List<DtItem> itemList = new ArrayList<>();
        List<String> labels = new ArrayList<>();

        //判断是否登录

        String userId = isLogined(request);
//        String userId = "1";
        if (userId==null){
            itemList = esGetItemx(pageNum, pageSize);
            return Result.ok().setData(itemList);
        }else {
            labels = esGetItemxx(pageNum, pageSize,userId);
            String labelstr = JSONObject.toJSONString(labels);
            Result result = esGetItemByLabelId(labelstr, pageNum, pageSize);
            return result;
        }


    }
    //登录用户uid获取
    public String isLogined(HttpServletRequest request){

        String authToken = request.getHeader("authToken");
        System.out.println(authToken);
        //获取参数
//        String userName = request.getParameter("userName");
        //校验用户名是否存在，如果不存在，说明用户未登录，不放过(UserDTO)redisTemplate.boundValueOps(authToken).get() == null)
        if(authToken == null || "".equals(authToken)) {
           return null;
        }else {
            try {
                Claims claims = jwtUtil.parseJwt(authToken);
                String userId = claims.getId();
                String subject = claims.getSubject();
                //已登录
                return userId;
            }catch (Exception e){
                return null;
            }
        }
    }
    //未登录推荐列表
    public List<DtItem> esGetItemx( Integer pageNum, Integer pageSize){
        //es查询item
        SearchRequest request = new SearchRequest();
        //指定index
        request.indices("dt_item");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        //查询
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        builder.query(matchAllQueryBuilder);


        //分页
        Integer start = (pageNum-1)*pageSize;

        builder.from(start);

        builder.size(pageSize);

        request.source(builder);
        List<DtItem> itemList = new ArrayList<>();

        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            SearchHits hits = response.getHits();
            long totalHits = hits.getTotalHits();
            Long totles = totalHits;


            for (SearchHit hit : hits) {
                //循环打印查询出来的结果以字符串的形式;
                String itemJson = hit.getSourceAsString();
                DtItem item = JSONObject.parseObject(itemJson, DtItem.class);
                itemList.add(item);
            }
            Long totlePages = totles % pageSize == 0 ? totles / pageSize : totles / pageSize + 1;
            System.out.println("总条数" + totles);
            System.out.println("总页数" + totlePages);
        }catch (Exception e){
            e.printStackTrace();
        }
        return itemList;
    }

    //已登录推荐标签列表
    @PostMapping("getLabels")
    public List<String> esGetItemxx(@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "2") Integer pageSize,String uid){

        String finalKey = uid+"-finalKey";
        Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.boundZSetOps(finalKey).reverseRangeWithScores(0, -1);
        Iterator<ZSetOperations.TypedTuple<Object>> iterator = set.iterator();
        List<String> list = new ArrayList<>();
        while (iterator.hasNext()){
            String sn = (String) iterator.next().getValue();
            list.add(sn);
        }
        return list;
    }
    //根据标签查询推荐列表
    @PostMapping("esGetItemByLabelId")
    @ApiOperation(value = "根据标签查询推荐列表")
    //参数类型：paramType body表示@RequestBody接收的参数，path表示路径传参/{xx},query表示@RequestParam接受的参数
    @ApiImplicitParams({
            @ApiImplicitParam(name="itemsn",value = "标签",required = true,paramType = "query"),
            @ApiImplicitParam(name="pageNum",value = "当前页数",required = true,paramType = "query"),
            @ApiImplicitParam(name="pageSize",value = "每页条数",required = true,paramType = "query")
    })
    public Result esGetItemByLabelId(@RequestParam("itemsn") String itemsn,@RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize",defaultValue = "3") Integer pageSize){
        List<String> strings = JSONObject.parseArray(itemsn, String.class);
        String serchWords="";
        if (strings.size()>=3){
            serchWords = strings.get(0)+","+strings.get(1)+","+strings.get(2);
        }else if(strings.size()==1){
            serchWords = strings.get(0);
        }else if (strings.size()==2){
            serchWords = strings.get(0)+","+strings.get(1);
        }
        System.out.println("搜索词"+serchWords);
        //es查询item
        SearchRequest request = new SearchRequest();
        //指定index
        request.indices("dt_item");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        //查询
//        MatchPhraseQueryBuilder mat = QueryBuilders.matchPhraseQuery("item_sn", serchWords);
        CommonTermsQueryBuilder item_sn = QueryBuilders.commonTermsQuery("item_sn", serchWords);

        builder.query(item_sn);


        //分页
        Integer start = (pageNum-1)*pageSize;

        builder.from(start);

        builder.size(pageSize);

        request.source(builder);
        List<DtItem> itemList = new ArrayList<>();

        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            SearchHits hits = response.getHits();
            long totalHits = hits.getTotalHits();
            Long totles = totalHits;


            for (SearchHit hit : hits) {
                //循环打印查询出来的结果以字符串的形式;
                String itemJson = hit.getSourceAsString();
                DtItem item = JSONObject.parseObject(itemJson, DtItem.class);
                itemList.add(item);
            }
            Long totlePages = totles % pageSize == 0 ? totles / pageSize : totles / pageSize + 1;
            System.out.println("总条数" + totles);
            System.out.println("总页数" + totlePages);
        }catch (Exception e){
            e.printStackTrace();
        }
        return Result.ok().setData(itemList) ;

    }
    //点击推荐商品后
    @PostMapping("click")
    @ApiOperation(value = "点击商品后执行的redis千人千面缓存业务")
    //参数类型：paramType body表示@RequestBody接收的参数，path表示路径传参/{xx},query表示@RequestParam接受的参数
    @ApiImplicitParam(name="itemSn",value = "商品标签",required = true,paramType = "query")
    public void click(@RequestParam("itemSn")String itemSn,HttpServletRequest request){
        //判断是否登录(itemSn 商品标签 例[1,2,3])
//        String userId = isLogined(request);
        String userId = "1";
        if (userId==null){

        }else {
            //设置浏览量缓存
            String key = userId+"-click";

            // 把 标签 转成 list
            List<String> strings = JSONObject.parseArray(itemSn, String.class);
            System.out.println(strings);
           // 获取当前 key 中 set 的 所有数据 如果 不存在 设置 初始值为1 如果 存在 +1
            // 查询出当前 key中 所有数据

            Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.boundZSetOps(key).rangeWithScores(0, -1);
            // set迭代器遍历
            Iterator<ZSetOperations.TypedTuple<Object>> iterator = set.iterator();
            // 获取set中的所有 value 存入集合中
            List<String> zsetList = new ArrayList<>();
            // 获取set中的所有 score 存入集合中

            while (iterator.hasNext()) {
                ZSetOperations.TypedTuple<Object> next = iterator.next();
                String value = (String)next.getValue();
                zsetList.add(value);
            }
            // 循环传来的 标签
            for (String sn : strings) {

                // 如果 当前 标签 已存在 就 在原来的基础上+1 否则 就 新建 score = 1
                if (zsetList.contains(sn)) {
                    // 当前key 的当前 值分数+1
                    redisTemplate.boundZSetOps(key).incrementScore(sn,1);
                } else {
                    // 否则 就新建value
                    redisTemplate.boundZSetOps(key).add(sn, 1);

                }
            }


            // 使用 reids+当前毫秒值 存入
            String timeKey = userId + "-time";
            //  标签为value 时间戳为 score
            Set<ZSetOperations.TypedTuple<Object>> typedTupleSet = new HashSet<ZSetOperations.TypedTuple<Object>>();
            // 存入zset中
            for (String s : strings) {
                ZSetOperations.TypedTuple<Object> typedTuple = new DefaultTypedTuple<Object>(s, Double.valueOf(System.currentTimeMillis()));
                typedTupleSet.add(typedTuple);
                redisTemplate.boundZSetOps(timeKey).add(typedTupleSet);
            }
            // 获取 最近访问时间 前三的
            Set<ZSetOperations.TypedTuple<Object>> accessTimeLatelyTopThree = redisTemplate.boundZSetOps(timeKey).reverseRangeWithScores(0, 2);
            // 获取 访问 次数最多的 前3个
            Set<ZSetOperations.TypedTuple<Object>> scoreTopThree = redisTemplate.boundZSetOps(key).reverseRangeWithScores(0, 2);
            Iterator<ZSetOperations.TypedTuple<Object>> scoreTopThreeIterator = scoreTopThree.iterator();
            List<String> scoreTopThreeList = new ArrayList<>();
            while (scoreTopThreeIterator.hasNext()) {
                scoreTopThreeList.add((String) scoreTopThreeIterator.next().getValue());
            }

//        然后获取访问时间最近的三个标签，然后查询一下缓存看这三个标签是否浏览量大于三，
//        删除浏览量小于三的标签（目的是排除用户一不小心点击到的情况），
            Iterator<ZSetOperations.TypedTuple<Object>> accessTimeLatelyTopThreeIterator = accessTimeLatelyTopThree.iterator();
            List<String> accessTimeLatelyTopThreeList = new ArrayList();
            while (accessTimeLatelyTopThreeIterator.hasNext()) {
                accessTimeLatelyTopThreeList.add((String) accessTimeLatelyTopThreeIterator.next().getValue());
            }
            // 查看 最近访问的三个标签在 key 中 是否存在 并且 在 key 的score <3 的 删除
            for (String sn : zsetList) {
               if(accessTimeLatelyTopThreeList.contains(sn) && redisTemplate.boundZSetOps(key).score(sn)<3){
                   accessTimeLatelyTopThreeList.remove(sn);
               }
            }


            /**
             * 然后将排名前三的标签以zset类型放入redis缓存，分数分别为3、2、1，
             * 然后将获取的用户最近访问的三个标签也放入这个redis缓存，分数统一设定为3，
             * 如果该标签已存在则将原有分数加3（这个缓存是用来定向推荐的，每半小时更新一次）
             * 只推荐其分数前三的标签
             * --------------
             * 如果 最近访问 在 推荐访问中的 存在 + 3
             */

            String finalKey = userId + "-finalKey";

            int num = 3;
            for (int i = 0; i < scoreTopThreeList.size(); i++) {
                // 点击量排名前三的标签以zset类型放入redis缓存，分数分别为3、2、1，
                redisTemplate.boundZSetOps(finalKey).add(scoreTopThreeList.get(i), num--);
            }

            // 循环 最近访问的前三
            for (String s : accessTimeLatelyTopThreeList) {
                if (scoreTopThreeList.contains(s)){
                    redisTemplate.boundZSetOps(finalKey).incrementScore(s,3);
                }else {
                    redisTemplate.boundZSetOps(finalKey).add(s,3);
                }
            }

        }

    }


}
